#include <stdexcept>
#include <boost/foreach.hpp>
#include <boost/utility/binary.hpp>
#include "latin1sucks.h++"

std::string UTF8ToBrokenLatin1(const std::string &utf8, bool ignore_wrong) {
	std::string result;
	enum {
		normal, utf8start, toomanybyte, unrepresentable
	} state = normal;
	unsigned char previous_utf8_thing;
	BOOST_FOREACH(char c, utf8) {
		unsigned char input = static_cast<unsigned char>(c);
		if ((input & BOOST_BINARY(10000000)) == 0) {
			result += c;
			if (state == toomanybyte)
				state = normal; // found end, phew.
			if (state == normal)
				continue;
			if (ignore_wrong) {
				state = normal;
				continue;
			}
			throw std::out_of_range("abnormal utf8 termination");
		}
		// start is 110 for two bytes, 1110 for three ...
		// and then follows the first bit of the codepoint.
		if ((input&BOOST_BINARY(11100000)) == BOOST_BINARY(11100000)) {
			// three bytes or more : unrepresentable.
			state = toomanybyte;
			if (ignore_wrong)
				continue;
			throw std::out_of_range("unrepresentable 3 byte char");
		}
		if ((input&BOOST_BINARY(11100000)) == BOOST_BINARY(11000000)) {
			// we have two byte. latin1 is encoded in utf8
			// using code points from 0 to ff.
			// so we should only have at most two bit on the right
			if (state != normal) {
				// we are already on a start : BAD !
				state = toomanybyte;
				if (ignore_wrong)
					continue;
				throw std::out_of_range("too many starts");
			}
			if ((input &~ BOOST_BINARY(11000011)) == 0) {
				previous_utf8_thing = input << 6;
				state = utf8start;
				continue;
			}
			// ok, we are above ff.
			state = unrepresentable;
			if (ignore_wrong)
				continue;
			throw std::out_of_range("unrepresentable character.");
		}
		if ((input&BOOST_BINARY(11000000)) == BOOST_BINARY(10000000)) {
			// we are on a continuation.
			if (state == toomanybyte)
				// more byte unrepresentable.
				continue;
			if (state == unrepresentable) {
				// 2 byte unrepresentable.
				state = normal;
				continue;
			}
			if (state == utf8start) {
				// good.
				result+= (previous_utf8_thing | 
					(input &~BOOST_BINARY(11000000)));
				state = normal;
				continue;
			} 
			// state can be normal  ...
			if (ignore_wrong)
				continue;
			throw std::out_of_range("continuation without start");
		}
	}
	return result;
}


// from http://discuss.joelonsoftware.com/default.asp?joel.3.325282.13
std::string Latin1toUTF8( const char* szStr )
{
  const unsigned char* pSource = (const unsigned char*)szStr;
  std::string strResult;
  int nLen = strlen( szStr );
  strResult.reserve( nLen + nLen/10 );
  int cSource = *(pSource);
  while ( cSource )
  {
    if ( cSource >= 128 )
    {
      strResult += (char)(((cSource&0x7c0)>>6) | 0xc0);
      strResult += (char)((cSource&0x3f) | 0x80);
    }
    else
      strResult += cSource;
    cSource = *(++pSource);
  }
  return strResult;
} 
